/*
 * @author: wizardc
 */

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace Dou.UI
{
    /// <summary>
    /// 使用 UGUI 的 RectMask2D 作为遮罩
    /// 将该组件挂到特效的 GO 上即可生效，对收集自身的所有父级遮罩组件来进行遮罩，不支持 RectMask2D 的 Padding 属性
    /// </summary>
    public class UIParticleRectMask2D : MonoBehaviour
    {
        private static readonly Vector3[] TempVector3Array = new Vector3[4];
        private static readonly Vector4 NoneVector3 = new Vector4(1, -1, 1, -1);
        
        private RectTransform _rectTransform;
        private Vector3 _lastPosition;
        private Vector2 _lastSizeDelta;
        private List<RectMask2D> _clippers = new List<RectMask2D>();

        void OnEnable()
        {
            _rectTransform = GetComponent<RectTransform>();
            RefreshClipRect();
        }

        void OnTransformParentChanged()
        {
            RefreshClipRect();
        }

        void OnCanvasHierarchyChanged()
        {
            RefreshClipRect();
        }
        
        private void LateUpdate()
        {
            if (_lastPosition != transform.position || _lastSizeDelta != _rectTransform.sizeDelta)
            {
                _lastPosition = transform.position;
                _lastSizeDelta = _rectTransform.sizeDelta;
                RefreshClipRect(false);
            }
        }

        void OnDisable()
        {
            _clippers.Clear();
        }

        private void RefreshClipRect(bool refreshClippers = true)
        {
            if (refreshClippers)
            {
                GetAllRectMask2D(_clippers);
            }
            var maskRect = GetClipWorldRect(_clippers);
            var renderers = GetComponentsInChildren<ParticleSystemRenderer>(true);
            foreach (var _render in renderers)
            {
                if (_render.material.HasProperty("_MaskRect"))
                {
                    _render.material.EnableKeyword("USE_UI_MASK_RECT");
                    // 注意这里传入的是事件坐标的最终裁剪Rect区域，Shader中会保存世界坐标信息传递给片段着色器进行比对
                    _render.material.SetVector("_MaskRect", maskRect);
                }
            }
        }

        private void GetAllRectMask2D(List<RectMask2D> clippers)
        {
            clippers.Clear();
            var rectMask2Ds = GetComponentsInParent<RectMask2D>(false);
            clippers.AddRange(rectMask2Ds);
        }

        private Vector4 GetClipWorldRect(List<RectMask2D> rectList)
        {
            if (rectList.Count == 0)
            {
                return NoneVector3;
            }
            Vector4 current = GetWorldRect(rectList[0].rectTransform);
            float xMin = current.x;
            float xMax = current.z;
            float yMin = current.y;
            float yMax = current.w;
            for (var i = 1; i < rectList.Count; ++i)
            {
                current = GetWorldRect(rectList[i].rectTransform);
                if (xMin < current.x)
                {
                    xMin = current.x;
                }
                if (yMin < current.y)
                {
                    yMin = current.y;
                }
                if (xMax > current.z)
                {
                    xMax = current.z;
                }
                if (yMax > current.w)
                {
                    yMax = current.w;
                }
            }
            if (xMax > xMin && yMax > yMin)
            {
                return new Vector4(xMin, yMin, xMax, yMax);
            }
            return NoneVector3;
        }

        private Vector4 GetWorldRect(RectTransform trans)
        {
            var result = Vector4.zero;
            trans.GetWorldCorners(TempVector3Array);
            result.x = TempVector3Array[0].x;
            result.y = TempVector3Array[0].y;
            result.z = TempVector3Array[2].x;
            result.w = TempVector3Array[2].y;
            return result;
        }
    }
}
